use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;

pub fn check_id_card(id_card_no: &str) -> String {
    if id_card_no.len() == 18 {
        return check_18(id_card_no);
    }
    if id_card_no.len() == 15 {
        return check_15(id_card_no);
    }
    WRONG.to_string()
}

fn check_15(id_card_no: &str) -> String {
    let (address, left_no) = id_card_no.split_at(6);
    let (birth, _) = left_no.split_at(6);
    let (_, gender) = left_no.split_at(2);
    let (flag, address) = get_address(address);
    if !flag {
        return WRONG.to_string();
    }
    let (flag, birth) = get_birth(birth);
    if !flag {
        return WRONG.to_string();
    }
    let gender = match u32::from_str(gender).unwrap() % 2 == 1 {
        true => "男",
        false => "女",
    };
    format!("{},{},{},{}", RIGHT, gender, birth, address)
}

fn check_18(id_card_no: &str) -> String {
    let (gender, birth, address) = {
        let (flag, gender) = check_all(id_card_no);
        if !flag {
            return WRONG.to_string();
        }
        let (address, left_no) = id_card_no.split_at(6);
        let (flag, address) = get_address(address);
        if !flag {
            return WRONG.to_string();
        }
        let (birth, _) = left_no.split_at(8);
        let (flag, birth) = get_birth(birth);
        if !flag {
            return WRONG.to_string();
        }
        (gender, birth, address)
    };
    format!("{},{},{},{}", RIGHT, gender, birth, address)
}

const WRONG: &str = "身份证号码错误";
const RIGHT: &str = "身份证号码正确";

fn check_all(id: &str) -> (bool, String) {
    let (id, check_digit) = id.split_at(17);
    let weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    let check_info = [1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2];
    let mut sum = 0;
    let mut gender = String::new();
    for (i, c) in id.chars().enumerate() {
        let c = u32::from(c) - 48;
        sum += c * weight[i];
        if i == 16 {
            gender = match c % 2 == 1 {
                true => "男".to_string(),
                false => "女".to_string(),
            }
        }
    }
    let check_digit = match check_digit == "X" {
        true => 10,
        false => u32::from_str(check_digit).unwrap(),
    };
    (check_digit == check_info[(sum % 11) as usize], gender)
}

fn get_birth(birth: &str) -> (bool, String) {
    let (y, left_str) = {
        if birth.len() == 8 {
            let (y, left_str) = birth.split_at(4);
            let y = u32::from_str(y).unwrap();
            (y, left_str)
        } else {
            let (y, left_str) = birth.split_at(2);
            let mut y = u32::from_str(y).unwrap();
            y += 1900;
            (y, left_str)
        }
    };
    let (m, d) = left_str.split_at(2);
    let m = u32::from_str(m).unwrap();
    let d = u32::from_str(d).unwrap();
    if !check_ymd(y, m, d) {
        return (false, String::new());
    }
    let m = if m < 10 {
        format!("0{}", m)
    } else {
        m.to_string()
    };
    let d = if d < 10 {
        format!("0{}", d)
    } else {
        d.to_string()
    };
    (true, format!("{}年{}月{}日", y, m, d))
}

fn check_ymd(y: u32, m: u32, d: u32) -> bool {
    if m > 12 || m == 0 {
        return false;
    }
    let max_d = match m {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
        4 | 6 | 9 | 11 => 30,
        2 => {
            if (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0)) {
                29
            } else {
                28
            }
        }
        _ => return false,
    };
    if d == 0 || d > max_d {
        return false;
    }
    true
}

fn get_address(address: &str) -> (bool, String) {
    let map = read_data();
    let province = i32::from_str(&format!("{}0000", &address[0..2])).unwrap();
    let city = i32::from_str(&format!("{}00", &address[0..4])).unwrap();
    let county = i32::from_str(address).unwrap();
    let county = map.get(&county);
    if county.is_none() {
        return (false, String::new());
    }
    let province = map.get(&province).unwrap();
    let city = map.get(&city);
    match city {
        Some(city) => {
            (true, format!("{}-{}-{}", province, city, county.unwrap()))
        }
        None => {
            (true, format!("{}-市辖区-{}", province, county.unwrap()))
        }
    }
}

fn read_data() -> HashMap<i32, String> {
    let workspace = PathBuf::from(find_workspace_root().unwrap());
    let file_path = "topic1/solutiont7/src/address.json";
    let path = workspace.join(file_path);
    let mut file = File::open(path).unwrap();
    let mut json = String::new();
    file.read_to_string(&mut json).unwrap();
    let map: HashMap<i32, String> = serde_json::from_str(&json).unwrap();
    map
}

fn find_workspace_root() -> Option<String> {
    let mut current_dir = env::current_dir().ok()?;
    let cargo_toml_path = current_dir.join("Cargo.toml");
    if cargo_toml_path.is_file() {
        let mut file = File::open(cargo_toml_path).unwrap();
        let mut json = String::new();
        file.read_to_string(&mut json).unwrap();
        if json.contains("[workspace]") {
            return Some(current_dir.to_str().unwrap().to_string());
        }
    }

    while current_dir.parent().is_some() {
        current_dir = current_dir.parent()?.to_path_buf();
        let cargo_toml_path = current_dir.join("Cargo.toml");
        if cargo_toml_path.is_file() {
            let mut file = File::open(cargo_toml_path).unwrap();
            let mut json = String::new();
            file.read_to_string(&mut json).unwrap();
            if json.contains("[workspace]") {
                return Some(current_dir.to_str().unwrap().to_string());
            }
        }
    }

    None
}